const _objToStrCall = (val: unknown, type: string) => {
  return Object.prototype.toString.call(val) === `[object ${type}]`;
};

/**判断是否是布尔值 */
const isBoolean = (val: unknown): val is boolean => {
  return _objToStrCall(val, "Boolean");
};

/**判断是否是字符串 */
const isString = (val: unknown): val is string => {
  return _objToStrCall(val, "String");
};

/**判断是否是数字，不包括字符串等表示的数字 */
function isNumber(val: unknown): val is number {
  return _objToStrCall(val, "Number");
}
/**判断是否是 NaN，避免原生 isNaN 函数将 undefined 等值也当成 NaN */
function isNaNNumber(val: unknown): boolean{
  return isNumber(val) && (val.toString() === 'NaN')
}

/**是否是非 NaN 数字 */
function isNotNaNNumber(val: unknown): boolean {
  return isNumber(val) && (val.toString() !== 'NaN')
}

/**判断是否是无穷大数值 Infinity */
function isInfinity(val: unknown): boolean{
  return isNumber(val) && (val.toString() === 'Infinity')
}

/**判断是否不是无穷大值 Infinity 的数字*/
function isNotInfinityNumber(val: unknown): boolean{
  return isNumber(val) && (val.toString() !== 'Infinity')
}

/**判断是否不是 NaN 且不是 Infinity 的数字 */
function isNotNaNOrInfinityNumber(val: unknown): boolean{
  return isNumber(val) && (val.toString() !== 'Infinity') && (val.toString() !== 'NaN')
}

/**判断一个数字是否是奇数 */
const isOddNumber = (val:number) => {
  return val%2!==0
}

/**判断一个数字是否是偶数数 */
const isEvenNumber = (val:number) => {
  return val%2===0
}

/**是否是整数 */
const isInt = (val: unknown): boolean => {
  if (isNumber(val)) {
    if (/^\d*\d$/.test(val.toString())) {
      return true;
    }
  }
  return false;
};

/**是一个 symbol 类型值 */
const isSymbol = (val: unknown): val is symbol => {
  return _objToStrCall(val, "Symbol");
}

/**是否是普通对象 */
const isObject = (val: any): val is Record<any, any> => {
  return val !== null && _objToStrCall(val, "Object");
};

/**是否是函数 */
const isFunction = (val: unknown): val is Function => {
  return typeof val === "function";
};

/**是否是数组 */
const isArray = (val: any): val is Array<any> => {
  return val && Array.isArray(val);
};


/**是否是 Int8Array 实例 */
const isInt8Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "Int8Array");
};


/**是否是 Uint8Array 实例 */
const isUint8Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "Uint8Array");
};

/**是否是 Uint8ClampedArray 实例 */
const isUint8ClampedArray = (val: any): val is Array<any> => {
  return _objToStrCall(val, "Uint8ClampedArray");
};

/**是否是 Int16Array 实例 */
const isInt16Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "Int16Array");
};

/**是否是 Uint16Array 实例 */
const isUint16Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "Uint16Array");
};

/**是否是 Int32Array 实例 */
const isInt32Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "Int32Array");
};

/**是否是 Uint32Array 实例 */
const isUint32Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "Uint32Array");
};

/**是否是 BigInt64Array 实例 */
const isBigInt64Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "BigInt64Array");
};

/**是否是 BigUnit64Array 实例 */
const isBigUnit64Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "BigUnit64Array");
};

/**是否是 Float32Array 实例 */
const isFloat32Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "Float32Array");
};

/**是否是 Float64Array 实例 */
const isFloat64Array = (val: any): val is Array<any> => {
  return _objToStrCall(val, "Float64Array");
};


/**是否是 ArrayBuffer 实例 */
const isArrayBuffer = (val: any): val is Array<any> => {
  return _objToStrCall(val, "ArrayBuffer");
};

/**是否是 DataView 实例 */
const isDataView = (val: any): val is Array<any> => {
  return _objToStrCall(val, "DataView");
};



/**是否是映射 */
const isMap = (val: unknown): val is Map<any, any> => {
  return _objToStrCall(val, "Map");
};


/**是否是 WeakMap 实例 */
const isWeakMap = (val: unknown): val is Map<any, any> => {
  return _objToStrCall(val, "WeakMap");
};

/**是否是 Set 实例 */
const isSet = (val: unknown): val is Set<any> => {
  return _objToStrCall(val, "Set");
};

/**是否是 WeakSet 实例 */
const isWeakSet = (val: unknown): val is Map<any, any> => {
  return _objToStrCall(val, "WeakSet");
};


/**是否是 WeakRef 实例 */
const isWeakRef = (val: any): val is Array<any> => {
  return _objToStrCall(val, "WeakRef");
};


/**是否是正则 */
const isRegExp = (val: unknown): val is RegExp => {
  return _objToStrCall(val, "RegExp");
};


/**是否是 Date 实例 */
const isDate = (val: unknown): val is Map<any, any> => {
  return _objToStrCall(val, "Date");
};

/**是否是 Error 实例 */
const isError = (val: unknown): val is Map<any, any> => {
  return (_objToStrCall(val, "Error")  || val instanceof Error);
};

/**是否是原始值 */
const isPrimitiver = (val: unknown):boolean => {
  return val === null ||
         typeof val === 'boolean' ||
         typeof val === 'number' ||
         typeof val === 'string' ||
         typeof val === 'symbol' || 
         typeof val === 'undefined';
}

function isPromise<T = any>(val: unknown): val is Promise<T> {
  return (
    _objToStrCall(val, "Promise") &&
    isObject(val) &&
    isFunction(val.then) &&
    isFunction(val.catch)
  );
}

interface PromiseLike<T> {
  then<TResult1 = T, TResult2 = never>(
    onfulfilled?:
      | ((value: T) => TResult1 | PromiseLike<TResult1>)
      | undefined
      | null,
    onrejected?:
      | ((reason: any) => TResult2 | PromiseLike<TResult2>)
      | undefined
      | null
  ): PromiseLike<TResult1 | TResult2>;
}

function isPromiseLike<T>(val: any): val is PromiseLike<T> {
  return (
    (val !== null && typeof val === "object") ||
    (typeof val === "function" && typeof val.then === "function")
  );
}

/**是否非 undefined */
function isDefined<T = unknown>(val?: T): val is T {
  return typeof val !== 'undefined';
}

/**是否是 undefined */
function isUndefined<T = unknown>(val?: T): val is T {
  return !isDefined(val);
}

/**是否是 null */
function isNull(val: unknown): val is null {
  return val === null;
}

/**
 * isUndefined 且 isNull 成立返回 true
 */
function isNullAndUndefined(val: unknown) {
  return isUndefined(val) && isNull(val);
}

/**
 * isUndefined 或 isNull 成立返回 true
 */
function isNullOrUndefined(val: unknown): val is null | undefined {
  return isUndefined(val) || isNull(val);
}

/**是否是 Buffer */
function isBuffer(val: unknown): val is Buffer{
  return Buffer.isBuffer(val);
}



export {
  isBoolean,
  isString,
  isNumber,
  isNaNNumber,
  isNotNaNNumber,
  isInfinity,
  isNotInfinityNumber,
  isNotNaNOrInfinityNumber,
  isOddNumber,
  isEvenNumber,
  isInt,
  isObject,
  isSymbol,
  isArray,
  isInt8Array,
  isUint8Array,
  isUint8ClampedArray,
  isInt16Array,
  isUint16Array,
  isInt32Array,
  isUint32Array,
  isBigInt64Array,
  isBigUnit64Array,
  isFloat32Array,
  isFloat64Array,
  isArrayBuffer,
  isDataView,

  isFunction,
  isMap,
  isWeakMap,
  isSet,
  isWeakSet,
  isWeakRef,
  isRegExp,
  isDate, 
  isError, 
  isPrimitiver,
  isPromise,
  isPromiseLike,
  isDefined,
  isUndefined,
  isNull,
  isNullAndUndefined,
  isNullOrUndefined,
  isBuffer,
};
